require "amadeusdsl/priorityqueue"

module Amadeusdsl
  module Algorithm
   
   class RouteFinder
     
     #Initialize RoutFinder
     #From = start airport
     #To = destination airport
     #Seats = amount of seats we need on the flight
     #Graph = graph of airports
     #Optimize = which parameter to optimize => price|duration|hops
     #start_date = start searching on this date
     #within = search within x days around start_date, default = 0
     def initialize(from,to,seats,c,graph,optimize,start_date,within)
       @from = from
       @to = to
       @seats = seats
       @g = graph
       @class = c
       @optimize = optimize
       @start_date = start_date
       @within = within
       @proc = proc{|x,y| x["value"] <= y["value"]}
       @queue = PriorityQueue.new(@proc)
       @solution = nil
       @cfinder = Amadeusdsl::Parser::ConnectionParser.new
       @sfinder = Amadeusdsl::Parser::AvailableParser.new
     end #end initialize
     
     def addPath(temp)
          if(temp["path"].last["to"] == @to) #We found a solution
           if(@solution == nil || @solution["value"] > temp["value"]) #We found a new best solution
            @solution = temp
           end
         else
           @queue.push(temp) #add partial solution to stack
         end
     end
     
     #Find the best route, result format: {"value" => integer, "path" => [connections]}
     def run()
       
       #Add initial paths
       connections = fetchConnections(@from) #fetch the connections
    
       connections.each do |connection| #for each connection, do
          addPath({"value" => calcValue(0,[connection]),"path" => [connection]})
       end #end loop
       
       #solution in 1 step
       if @solution != nil
         return @solution
       end
       
       if @queue.size == 0
         return nil
       end #end if
       
       loop{
         current_best = @queue.pop
         
         #stop if best solution is found
         if(@solution != nil && @proc[@solution,current_best]) 
           break 
         end #end if
        
     
         connections = fetchConnections(current_best["path"].last["to"])
         connections.each do |connection|

           #skip if next flight departs before previous is landed
           if((connection["departure"].to_i - (current_best["path"].last["departure"].to_i + current_best["path"].last["duration"].to_i + 3600)) < 0)
             next
           end
           
           #skip paths with loops
           if(current_best["path"].index{|x| x["from"] == connection["to"]} != nil)
             next
           end
           
           new_path = current_best["path"] + [connection]
           addPath({"value" => calcValue(current_best["value"],new_path), "path" => new_path})
           
         end #end do
       }
       
       return @solution
     end # end run
     
     def calcValue(value,path)
       case @optimize
       when "hops"
         return value + 1
       when "duration"
         return (path.last["departure"].to_i + path.last["duration"].to_i) - path.first["departure"].to_i
       else
         return value + path.last[@optimize]
       end #end case
     end #end calcvalue
     
     
     def fetchConnections(airport)
       result = []
       #find all outgoing airport flights

       @g[airport].each do |node|
         #multi-day routes
         (-@within..@within).each do |day|
           @cfinder.FromCode(airport).ToCode(node).On(getDay(day).strftime("%Y-%m-%d"))._execute.each do |connection|
             seats = @sfinder.On(connection["departure"].strftime("%Y-%m-%d")).Class(@class).Flight(connection["flight"]).execute
             if seats["amount"] < @seats
              next
             end #end if
             connection["price"] = seats["price"]
             connection["seats"] = seats["amount"]
             connection["class"] = @class
             result << connection
           end #end do seatfinder
         end #end do connectionfinder
       end #end do days
       
       return result
     end #end def
    
      def getDay(day)
        date = Time.at(@start_date - Time.at(day * 3600 * 24))
        return date
      end #end def
 
    end
     

  end #end module Algorithm
end #end module Amadeusdsl
